package com.sleepcamel.resteasyConfigurator.model;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import org.eclipse.swt.widgets.Shell;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.sleepcamel.exporter.DBExporter;
import com.sleepcamel.rest.domain.BaseBean;
import com.sleepcamel.rest.domain.JavaClass;
import com.sleepcamel.rest.domain.Mapping;
import com.sleepcamel.rest.domain.Project;
import com.sleepcamel.rest.domain.ProjectPackage;
import com.sleepcamel.rest.domain.TreeNode;
import com.sleepcamel.rest.domain.filegenerator.ProjectFilesGenerator;
import com.sleepcamel.rest.domain.method.RESTMethod;
import com.sleepcamel.resteasyConfigurator.model.dbwizard.DBWizardModel;
import com.sleepcamel.resteasyConfigurator.util.ClassProvider;
import com.sleepcamel.resteasyConfigurator.util.GSONClassFieldSerializer;
import com.sleepcamel.resteasyConfigurator.util.GSONHierarchySerializer;


public class App extends BaseBean{

	private static App instance;

	private App(){}
	
	public static App getInstance(){
		if ( instance == null )
			instance = new App();
		return instance;
	}
	
	protected UserPreferences preferences = new UserPreferences();

	private Project currentProject;
	private File currentProjectFile;
	private boolean currentProjectLoaded = false;

	private DBWizardModel dbWizardModel = new DBWizardModel();

	private Shell shell;

	private boolean hasGeneratedFilesFromDB = true;

	private DBExporter exporter;

	private File lastSavedDirectory;
	
	public UserPreferences getPreferences() {
		return preferences;
	}

	public void setPreferences(UserPreferences preferences) {
		this.preferences = preferences;
	}

	public Project getCurrentProject() {
		return currentProject;
	}

	public void setCurrentProject(Project currentProject) {
		setCurrentProjectLoaded(currentProject != null);
		firePropertyChange("currentProject", this.currentProject, this.currentProject = currentProject);
	}

	public boolean isCurrentProjectLoaded() {
		return currentProjectLoaded;
	}

	public void setCurrentProjectLoaded(boolean currentProjectLoaded) {
		firePropertyChange("currentProjectLoaded", this.currentProjectLoaded, this.currentProjectLoaded = currentProjectLoaded);
		if ( !currentProjectLoaded )
			currentProjectFile = null;
	}

	public boolean loadProject(File file) {
		if ( file == null )
			return false;
		try {
			BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
			StringBuffer sb = new StringBuffer();
			String readLine = bufferedReader.readLine();
			while(readLine != null){
				sb.append(readLine);
				readLine = bufferedReader.readLine();
			}
			bufferedReader.close();
			
			Gson gson = getGsonBuilder().create();
			
			Project project = gson.fromJson(sb.toString(),Project.class);
			loadProject(project, file);
			return true;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	public void loadProject(Project project, File projectFile){
		// GSON doesn't call setters, add parent to packages, mappings and methods
		// Just in case
		project.fixParentHood();
		
		setCurrentProject(project);
		currentProjectFile = projectFile;
	}
	
	private GsonBuilder getGsonBuilder() {
		GsonBuilder gsonBuilder = new GsonBuilder();
		gsonBuilder.registerTypeAdapter(TreeNode.class, new GSONHierarchySerializer<TreeNode>());
		gsonBuilder.registerTypeAdapter(Class.class, new GSONClassFieldSerializer());
		return gsonBuilder;
	}

	public void saveProject(File fileToSave) {
		if ( fileToSave == null )
			return;
		try {
			
			Gson gson = getGsonBuilder().create();
			
			String json = gson.toJson(currentProject);
			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(fileToSave));
			bufferedWriter.write(json);
			bufferedWriter.close();
			currentProjectFile = fileToSave;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void generateAndSaveSources(File directoryToSave) throws IOException {
		if ( directoryToSave == null )
			return;
		setLastSavedDirectory(directoryToSave);
		ProjectFilesGenerator projectFilesGenerator = new ProjectFilesGenerator(currentProject, directoryToSave);
		projectFilesGenerator.generate();
	}

	public String currentProjectPath() {
		if ( currentProjectFile == null )
			return "";
		return currentProjectFile.getAbsolutePath();
	}

	public File getCurrentProjectFile() {
		return currentProjectFile;
	}

	public void loadNewProject() {
		Project restProject = new Project();
		restProject.setBaseMapping("/rest");
		restProject.setName("REST Project");
		
		ProjectPackage comPackage = new ProjectPackage("com");
		restProject.addChild(comPackage);
		
		ProjectPackage sleepCamelPackage = new ProjectPackage("sleepcamel");
		comPackage.addChild(sleepCamelPackage);
		
		ProjectPackage examplePackage = new ProjectPackage("example");
		sleepCamelPackage.addChild(examplePackage);
		
		Mapping mapping = new Mapping();
		examplePackage.addChild(mapping);

		RESTMethod restMethod = new RESTMethod();
		mapping.addChild(restMethod);
		
		loadProject(restProject, null);
	}

	public DBWizardModel getDBConnectionModel() {
		return dbWizardModel ;
	}

	public void generateFilesFromDB() {
		exporter = new DBExporter();
		exporter.setJdbcUtils(dbWizardModel.getFirstPage().getJdbcUtils());
		exporter.setModelDirectory(dbWizardModel.getFirstPage().getOutputDirectory());
		exporter.setPackageName(dbWizardModel.getFirstPage().getPackageName());
		exporter.setProjectName(dbWizardModel.getFirstPage().getProjectName());
		setLastSavedDirectory(dbWizardModel.getFirstPage().getOutputDirectory());
		
		exporter.generateModelFiles();
		dbWizardModel.setComplete(true);
	}

	public void loadProjectGeneratedFromDB() {
		for(JavaClass generatedJavaClass:exporter.getGeneratedJavaClasses()){
			ClassProvider.getInstance().registerJavaClass(generatedJavaClass);
		}
		loadProject(exporter.generateRESTProject(), null);
	}
	
	public boolean hasGeneratedFilesFromDB() {
		return hasGeneratedFilesFromDB;
	}

	public void setShell(Shell shell) {
		this.shell = shell;
	}

	public Shell getShell() {
		return shell;
	}

	public void closeCurrentProject() {
		setCurrentProject(null);
		currentProjectFile = null;
	}

	public File getLastSavedDirectory() {
		return lastSavedDirectory;
	}

	public void setLastSavedDirectory(File lastSavedDirectory) {
		this.lastSavedDirectory = lastSavedDirectory;
	}

}
